﻿using System;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;

using Blackhole.Debug;

using iTunesPwned.Core.iTunes;
using iTunesPwned.Core.Plugin;

namespace iTunesPwned.Core
{
    public class iEngine : IDisposable
    {
        public RegisterTabItemDelegate RegisterTabItemHandler;
        public RegisterMenuItemDelegate RegisterMenuItemHandler;

        private DataSet m_internalDataBase;
        private iTunesPlayer m_itunesPlayer;
        private SqlConnection m_sqlConnection;
        private Dictionary<string, IPlugin> m_loadedPlugins;
        private Dictionary<string, SqlDataAdapter> m_sqlAdapters;

        public iEngine()
        {
            Garbage.Attach(new DebugViewLogger());

            this.m_loadedPlugins = new Dictionary<string, IPlugin>();

            this.m_sqlAdapters = new Dictionary<string, SqlDataAdapter>();

            this.m_internalDataBase = new DataSet();
        }

        public void Init()
        {
            try
            {
                SqlDataAdapter adapter;
                SqlCommandBuilder builder;

                this.m_sqlConnection = new SqlConnection("Server=localhost\\itunespwned;Database=itunespwned;User Id=sa;Password=iTunes!1234");

                adapter = new SqlDataAdapter("SELECT * FROM dbo.Settings", this.m_sqlConnection);

                builder = new SqlCommandBuilder(adapter);

                adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

                adapter.Fill(this.m_internalDataBase, "dbo.Settings");

                this.m_internalDataBase.Tables["dbo.Settings"].RowChanged += new DataRowChangeEventHandler(this.iEngine_RowChanged);

                this.m_internalDataBase.Tables["dbo.Settings"].RowDeleted += new DataRowChangeEventHandler(this.iEngine_RowDeleted);

                this.m_sqlAdapters.Add("dbo.Settings", adapter);

                adapter = new SqlDataAdapter("SELECT * FROM dbo.Library", this.m_sqlConnection);

                builder = new SqlCommandBuilder(adapter);

                adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

                adapter.Fill(this.m_internalDataBase, "dbo.Library");

                this.m_internalDataBase.Tables["dbo.Library"].RowChanged += new DataRowChangeEventHandler(this.iEngine_RowChanged);

                this.m_internalDataBase.Tables["dbo.Library"].RowDeleted += new DataRowChangeEventHandler(this.iEngine_RowDeleted);

                this.m_sqlAdapters.Add("dbo.Library", adapter);

                this.m_itunesPlayer = new iTunesPlayer(this.m_internalDataBase.Tables["dbo.Library"]);

                this.LoadPlugins(Environment.CurrentDirectory);

                //this.m_itunesPlayer.Verify();

                //if (this.m_itunesPlayer.NeedsUpdate())
                //    this.m_itunesPlayer.Update();
            }
            catch (SqlException e)
            {
                Garbage.Log(this, new LoggerArguements(LoggerArguements.LoggerLevels.All, "SQL error: {0}\n{1}", e.Message, e.StackTrace));
            }
        }

        private void iEngine_RowChanged(object sender, DataRowChangeEventArgs e)
        {
            DataTable dataTable = (DataTable)sender;

            this.m_sqlAdapters[dataTable.TableName].Update(dataTable);
        }

        private void iEngine_RowDeleted(object sender, DataRowChangeEventArgs e)
        {
            DataTable dataTable = (DataTable)sender;

            this.m_sqlAdapters[dataTable.TableName].Update(dataTable);
        }

        private void LoadPlugins(string pluginPath)
        {
            IPlugin plugin;

            foreach (string file in Directory.GetFiles(pluginPath, "*.dll"))
            {
                try
                {
                    plugin = PluginCreator.CreateInstance(file);

                    if (plugin == null)
                        continue;

                    Garbage.Log(this, new LoggerArguements(LoggerArguements.LoggerLevels.All, "Loaded plugin {0}", file));

                    plugin.RegisterTabItemHandler += this.RegisterTabItemHandler;

                    plugin.RegisterMenuItemHandler += this.RegisterMenuItemHandler;

                    if (plugin is ISettings)
                    {
                        ISettings settings = (ISettings)plugin;

                        settings.UpdateSettings += new UpdateSettingsDelegate(UpdateSettingsHandler);

                        DataRow settingsRow = this.m_internalDataBase.Tables["dbo.Settings"].Rows.Find(plugin.Identifier);

                        if (settingsRow == null)
                        {
                            settingsRow = this.m_internalDataBase.Tables["dbo.Settings"].NewRow();

                            settingsRow["identifier"] = plugin.Identifier;

                            this.m_internalDataBase.Tables["dbo.Settings"].Rows.Add(settingsRow);
                        }

                        settings.Deserialize(settingsRow["value"] as string);
                    }

                    if (plugin is IPlayer)
                    {
                        IPlayer player = (IPlayer)plugin;

                        player.Player = this.m_itunesPlayer;
                    }

                    plugin.Init();

                    this.m_loadedPlugins.Add(plugin.Name, plugin);
                }
                catch (Exception e)
                {
                    Garbage.Log(this, new LoggerArguements(LoggerArguements.LoggerLevels.All, "Error loading {0}: {1}", file, e.Message));
                }
            }
        }

        private void UpdateSettingsHandler(object sender)
        {
            DataRow settingsRow = this.m_internalDataBase.Tables["dbo.Settings"].Rows.Find(((IPlugin)sender).Identifier);

            settingsRow["value"] = ((ISettings)sender).Serialize();
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
        }

        ~iEngine()
        {
            Dispose(false);
        }

        public void Dispose(bool bUserDisposed)
        {
            if (bUserDisposed)
            {
                Garbage.Shutdown();

                foreach (KeyValuePair<string, SqlDataAdapter> pair in this.m_sqlAdapters)
                {
                    this.m_sqlAdapters[pair.Key].Dispose();
                }

                GC.SuppressFinalize(this);
            }
        }

        #endregion
    }
}